import { MongoClient } from "mongodb";

let clients: Record<string, MongoClient> = {};

export function connect(urls: string | { name: string; url: string }[]) {
  if (typeof urls === "string") {
    clients["0"] = new MongoClient(urls, {
      appName: "strategy-receiver",
    });
  } else if (Array.isArray(urls)) {
    for (let i = 0, len = urls.length; i < len; i++) {
      const item = urls[i];
      if (typeof item === "string") {
        clients[String(i)] = new MongoClient(item, {
          appName: "strategy-receiver",
        });
      } else {
        if (item.name != null && item.url != null) {
          clients[item.name] = new MongoClient(item.url, {
            appName: "strategy-receiver",
          });
        }
      }
    }
  }
}

export function close(name = "0") {
  const client = clients[name];
  if (client != null) {
    client.close().then();
    delete clients[name];
  }
}

export function closeAll() {
  for (const key in clients) {
    const client = clients[key];
    if (client != null) {
      client.close().then();
    }
  }
  clients = {};
}

/**
 * 返回 MongoDb 的集合，如果集合不存在则创建
 * @param name 集合名称
 * @param dbName 数据库名称
 * @param clientName 连接名称
 * @returns
 */
export function connection(name: string, dbName?: string, clientName = "0") {
  return clients[clientName].db(dbName).collection(name);
}

function getProjection(findOption: FindOption) {
  let projection;
  if (findOption.fields != null && findOption.fields.length > 0) {
    projection = findOption.fields.reduce(
      (acc: Record<string, number>, cur: string) => {
        if (cur.startsWith("-")) {
          acc[cur.substring(1)] = 0;
        } else {
          acc[cur] = 1;
        }
        return acc;
      },
      {},
    );
  }
  return projection;
}

/** 查询选项 */
type FindOption = {
  /** 集合名称 */
  connection: string;
  /** 查询条件 */
  filter?: Record<string, any>;
  /** 数据库名称 */
  db?: string;
  /** 连接名称 */
  client?: string;
  /** 查询字段 */
  fields?: string[];
  /** 如果查询单字段, 是否根据该字段平铺, 默认: false */
  flat?: boolean;
  /** 排序, eg: { field: 1, field2: -1 } */
  sort?: Record<string, number>;
};

/**
 * 查询数据
 * @param findOption - 查询参数
 * @returns
 */
export async function find(findOption: FindOption) {
  const conn = connection(
    findOption.connection,
    findOption.db,
    findOption.client || "0",
  );
  let findCursor = conn.find(findOption.filter || {}, {
    projection: getProjection(findOption),
  });
  if (findOption.sort) {
    findCursor = findCursor.sort(findOption.sort as any);
  }
  const res = [];
  for await (const item of findCursor) {
    if (findOption.flat && findOption.fields != null) {
      res.push(item[findOption.fields[0]]);
    } else {
      res.push({ ...item });
    }
  }
  await findCursor.close();
  return res;
}

/**
 * 查询单条数据
 * @param findOption - 查询参数
 * @returns - 如果找到数据则返回该数据，否则返回 null
 */
export async function findOne(findOption: FindOption) {
  const conn = connection(
    findOption.connection,
    findOption.db,
    findOption.client || "0",
  );
  const res = await conn.findOne(findOption.filter || {}, {
    projection: getProjection(findOption),
  });
  return res;
}
